//rvref.cpp -- simple uses of rvalue references
//lambda0.cpp -- using lambda expreessions
//lambda1.cpp -- use captured variables
//wrapped.cpp -- using a function wrapper as an argument
//variadic1.cpp -- using recursion to unpack a parameter pack
#include <iostream>
#include "chapter18_useless.h"
#include <vector>
#include <algorithm>
#include <cmath>
#include <ctime>
#include "chapter18_somedefs.h"
#include <functional>
#include <string>

void Example_18_1();
void Example_18_2();
void Example_18_4();
void Example_18_5();
void Example_18_8();
void Example_18_9();
void Example_18_10();

//int main()
//{
//	Example_18_10();
//	return 0;
//}

inline double f(double tf) { return 5.0*(tf - 32.0) / 9.0; };

void Example_18_1()
{
	using namespace std;
	double tc = 21.5;
	double && rd1 = 7.07;
	double && rd2 = 1.8*tc + 32.0;
	double && rd3 = f(rd2);
	cout << " tc value and address: " << tc << ", " << &tc << endl;
	cout << "rd1 value and address: " << rd1 << ", " << &rd1 << endl;
	cout << "rd2 value and address: " << rd2 << ", " << &rd2 << endl;
	cout << "rd3 value and address: " << rd3 << ", " << &rd3 << endl;
	cin.get();
	return;
}

void Example_18_2()
{
	using namespace std;
	Useless one(10, 'x');
	Useless two = one;			//calls copy constructor
	Useless three(20, 'o');
	Useless four(one + three);		//calls operator+(), move constructor
	cout << "object one: ";
	one.ShowData();
	cout << "object two: ";
	two.ShowData();
	cout << "object three: ";
	three.ShowData();
	cout << "object four: ";
	four.ShowData();
	return;
}

const long Size1 = 39L;
const long Size2 = 100 * Size1;
const long Size3 = 100 * Size2;
bool f3(int x) { return x % 3 == 0; }
bool f13(int x) { return x % 13 == 0; }

void Example_18_4()
{
	using namespace std;
	vector<int> numbers(Size1);

	srand(time(0));
	generate(numbers.begin(), numbers.end(), rand);

	//using function pointers
	cout << "Sample size = " << Size1 << '\n';

	int count3 = count_if(numbers.begin(), numbers.end(), f3);
	cout << "Count of numbers divisible by 3: " << count3 << '\n';
	int count13 = count_if(numbers.begin(), numbers.end(), f13);
	cout << "Count of number divisible by 13: " << count13 << "\n\n";

	//increase number of numbers
	numbers.resize(Size2);
	generate(numbers.begin(), numbers.end(), rand);
	cout << "Sample size = " << Size2 << '\n';
	//using a functor
	class f_mod
	{
	private:
		int dv;
	public:
		f_mod(int d=1):dv(d) {}
		bool operator()(int x) { return x %dv == 0; }
	};

	count3 = count_if(numbers.begin(), numbers.end(), f_mod(3));
	cout << "Count of numbers divisible by 3: " << count3 << '\n';
	count13 = count_if(numbers.begin(), numbers.end(), f_mod(13));
	cout << "Count of number divisible by 13: " << count13 << "\n\n";

	//increase number of numbers again
	numbers.resize(Size3);
	generate(numbers.begin(), numbers.end(), rand);
	cout << "Sample size = " << Size3 << '\n';
	//using lambdas
	count3 = count_if(numbers.begin(), numbers.end(), [](int x) {return x % 3 == 0; });
	cout << "Count of numbers divisible by 3: " << count3 << '\n';
	count13 = count_if(numbers.begin(), numbers.end(), [](int x) {return x % 13 == 0; });
	cout << "Count of number divisible by 13: " << count13 << "\n";

	return;
}

const long Size390000L = 390000L;
void Example_18_5()
{
	using namespace std;
	vector<int> numbers(Size390000L);

	srand(time(0));
	generate(numbers.begin(), numbers.end(), rand);
	cout << "Sample size = " << Size390000L << '\n';
	//using lambdas
	int count3 = count_if(numbers.begin(), numbers.end(), [](int x) {return x % 3 == 0; });
	cout << "Count of numbers divisible by 3: " << count3 << '\n';
	int count13 = 0;
	for_each(numbers.begin(), numbers.end(), [&count13](int x) {count13 += x % 13 == 0; });
	cout << "Count of numbers divisible by 13: " << count13 << '\n';
	//using a single lambda
	count3 = count13 = 0;
	for_each(numbers.begin(), numbers.end(),
		[&](int x) {count3 += x % 3 == 0; count13 += x % 13 == 0; });
	cout << "Count of numbers divisible by 3: " << count3 << "\n";
	cout << "Count of numbers divisible by 13: " << count13 << '\n';
	return;
}

double dub(double x) { return 2.0*x; }
double square1(double x) { return sqrt(x); }

void Example_18_8()
{
	using namespace std;
	double y = 1.21;
	function<double(double)> ef1 = dub;
	function<double(double)> ef2 = square1;
	function<double(double)> ef3 = Fq(10.0);
	function<double(double)> ef4 = Fp(10.0);
	function<double(double)> ef5 = [](double u) {return u*u; };
	function<double(double)> ef6 = [](double u) {return u + u / 2.0; };
	cout << "Function pointer dub:\n";
	cout << "  " << use_f(y, ef1) << endl;
	cout << "Function pointer square1:\n";
	cout << "  " << use_f(y, ef2) << endl;
	cout << "Function object Fp:\n";
	cout << "  " << use_f(y, ef3) << endl;
	cout << "Function object Fq:\n";
	cout << "  " << use_f(y, ef4) << endl;
	cout << "Lambda expression 1:\n";
	cout << "  " << use_f(y, ef5) << endl;
	cout << "Lambda expression 2:\n";
	cout << "  " << use_f(y, ef6) << endl;
	return;
}

//definition for 0 parameters -- terminating call
void show_list3() { }
//definition for 1 or more parameters
template<typename T, typename... Args>
void show_list3(T value, Args... args)
{
	std::cout << value << ", ";
	show_list3(args...);
}
void Example_18_9()
{
	using namespace std;
	int n = 14;
	double x = 2.71828;
	string mr = "Mr.String object!";
	show_list3(n, x);
	show_list3(x*x, '!', 7, mr);
	return;
}

//definition for 0 parameters
void show_list() { }
//definition for 1 parameters
template<typename T>
void show_list(const T& value)
{
	cout << value << '\n';
}
//definition for 2 or more parameters
template<typename T, typename...Args>
void show_list(const T& value, const Args&... args)
{
	cout << value << ", ";
	show_list(args...);
}

void Example_18_10()
{
	using namespace std;
	int n = 14;
	double x = 2.71828;
	string mr = "Mr. String objects!";
	show_list(n, x);
	show_list(x*x, '!', 7, mr);
	return;
}